package netserver

import (
	"container/list"
	"fmt"
	"iot-cloud-server/mongodb"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
	//	"time"
)

type BufPool struct {
	resource chan []byte
	New      func() []byte
}

func (pool *BufPool) Init(size int, newfunc func() []byte) {
	pool.resource = make(chan []byte, size)
	pool.New = newfunc
}

func (pool *BufPool) Put(r []byte) {
	pool.resource <- r
}

func (pool *BufPool) Get() []byte {
	if len(pool.resource) == 0 {
		r := pool.New()
		if r != nil {
			pool.resource <- r
		}
	}

	return <-pool.resource
}

func bufPoolNew() []byte {
	return make([]byte, 4096)
}

type Pkg struct {
	RemoteAddr net.Addr
	LocalAddr  net.Addr
	Req        []byte
	Rsp        []byte
	CustomFlag uint64
}

type Processor interface {
	Init() error
	CheckInput([]byte) int64
	Process(Pkg *Pkg, Conn *net.TCPConn) error
	Destroy()
}

type Svr struct {
	DB          *mongodb.MongoServer
	RecvBufSize int

	tcp  []*net.TCPListener
	proc Processor

	bufPool BufPool

	reqcnt    int32
	maxreqcnt int32

	wg *sync.WaitGroup
}

type SvrIntf struct {
	Network string
	Addr    string
}

func NewSvr(db *mongodb.MongoServer, wg *sync.WaitGroup, intf []SvrIntf, proc Processor, recvBufSize int, maxreqcnt int32) (*Svr, error) {
	svr := new(Svr)
	svr.proc = proc
	svr.maxreqcnt = maxreqcnt
	svr.RecvBufSize = recvBufSize
	svr.bufPool.Init(1024*20, bufPoolNew)
	svr.wg = wg
	svr.DB = db
	if recvBufSize == 0 {
		svr.RecvBufSize = 4096
	}

	for _, v := range intf {
		switch v.Network {
		case "tcp", "tcp4", "tcp6":
			{
				tcpaddr, e := net.ResolveTCPAddr(v.Network, v.Addr)
				if e != nil {
					return nil, e
				}
				tcpln, e := net.ListenTCP(v.Network, tcpaddr)
				if e != nil {
					return nil, e
				}
				svr.tcp = append(svr.tcp, tcpln)
			}
		default:
		}
	}

	return svr, nil
}

func (svr *Svr) procTCPConn(c *net.TCPConn) error {
	defer svr.wg.Done()
	buf := make([]byte, svr.RecvBufSize)
	cnt := 0

	for {
		rc, e := c.Read(buf[cnt:])
		if e != nil {
			return e
		}
		cnt += rc
		rc = int(svr.proc.CheckInput(buf[:cnt]))
		switch {
		case rc > 0:
			{
				pkg := new(Pkg)
				pkg.RemoteAddr = c.RemoteAddr()
				pkg.LocalAddr = c.LocalAddr()
				pkg.Req = buf[:cnt]
				buf = make([]byte, cap(buf))
				cnt = 0

				svr.wg.Add(1)
				go func() {
					svr.wg.Done()
					svr.proc.Process(pkg, c)
					/*
						if e != nil {
						}
						if pkg.Rsp != nil {
							c.Write(pkg.Rsp)
					*/
				}()
			}
		case rc == 0:
			{
				if len(buf) == cap(buf) {
					newbuf := make([]byte, 2*len(buf))
					copy(newbuf, buf)
					buf = newbuf
				}
			}
		case rc < 0:
			{
				c.Close()
				break
			}
		default:
		}
	}
	return nil
}

func (svr *Svr) Run() error {

	e := svr.proc.Init()
	if e != nil {
		return e
	}

	for _, listenConn := range svr.tcp {
		svr.wg.Add(1)
		go func() {
			defer svr.wg.Done()
			connList := list.New()
			for {
				conn, e := listenConn.AcceptTCP()
				if e != nil {
					fmt.Println("accept failed:", e)
					var next *list.Element
					for v := connList.Front(); v != nil; v = next {
						next = v.Next()
						conn := v.Value.(*net.TCPConn)
						conn.Close()
						connList.Remove(v)
					}
					return
				}
				connList.PushBack(conn)
				svr.wg.Add(1)
				go svr.procTCPConn(conn)
			}
		}()
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM)
	s := <-c
	fmt.Println("recv sig: ", s)

	for _, listenConn := range svr.tcp {
		listenConn.Close()
	}

	svr.wg.Wait()
	fmt.Println("all goroutines done")
	svr.proc.Destroy()

	fmt.Println("proc destroyed")

	return nil
}
